/**
 * 
 */
package org.jenkon.map;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;

import org.jenkon.decorator.MapDecorator;

/**
 * {@link Map} implementing to observe this.
 * 
 * @author Jens Konopka
 * 
 * @param <K> the type of the key
 * @param <V> the type of the value
 */
public class ObservableMap<K, V> extends MapDecorator<K, V> {

	/**
	 * the {@link Collection} with all {@link MapObserver} to observe.
	 */
	private final Collection<MapObserver<K, V>> observers = new ArrayList<MapObserver<K, V>>();

	/**
	 * @param map decorating {@link Map}
	 */
	public ObservableMap(final Map<K, V> map) {
		super(map);
	}

	/**
	 * @param observer {@link MapObserver} to add to the {@link #observers}
	 */
	public void addObserver(final MapObserver<K, V> observer) {
		observers.add(observer);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.jenkon.decorator.MapDecorator#put(java.lang.Object, java.lang.Object)
	 */
	@Override
	public V put(final K key, final V value) {
		final V result = super.put(key, value);
		for (final MapObserver<K, V> observer : observers) {
			observer.put(key, value, result);
		}
		return result;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.jenkon.decorator.MapDecorator#putAll(java.util.Map)
	 */
	@Override
	public void putAll(final Map<? extends K, ? extends V> m) {
		super.putAll(m);
		for (final MapObserver<K, V> observer : observers) {
			observer.put(m);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.jenkon.decorator.MapDecorator#clear()
	 */
	@Override
	public void clear() {
		super.clear();
		for (final MapObserver<K, V> observer : observers) {
			observer.clear();
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.jenkon.decorator.MapDecorator#remove(java.lang.Object)
	 */
	@Override
	public V remove(final Object key) {
		final V result = super.remove(key);
		for (final MapObserver<K, V> observer : observers) {
			observer.remove(key, result);
		}
		return result;
	}

	/**
	 * Observer for {@link ObservableMap}.
	 * 
	 * @author Jens Konopka
	 * 
	 * @param <K> the type of the key
	 * @param <V> the type of the value
	 */
	public interface MapObserver<K, V> extends Serializable {

		/**
		 * Put a key and value to the {@link Map}.
		 * 
		 * @param key Key to add
		 * @param value Value to add
		 * @param result the result of {@link Map#put(Object, Object)}
		 * @see Map#put(Object, Object)
		 */
		void put(final K key, final V value, final V result);

		/**
		 * Put a {@link Map} to the currently values.
		 * 
		 * @param m {@link Map} to add
		 * @see Map#putAll(Map)
		 */
		void put(final Map<? extends K, ? extends V> m);

		/**
		 * Clear the {@link Map}.
		 * 
		 * @see Map#clear()
		 */
		void clear();

		/**
		 * Remove a value from {@link Map} with key and get the value as result.
		 * 
		 * @param key the key of value to remove
		 * @param result the result of removed value
		 * @see Map#remove(Object)
		 */
		void remove(final Object key, final V result);

	}

}
